﻿
if (typeof(Ext) === "undefined") {
    Ext = {};
}

//返回一个函数，该函数的作用是将一个对象的成员拷贝给另一个对象
Ext.apply = (function () {

    for (var key in { valueOf: 1 }) {//这样做极有可能是为了判断不同的浏览器
        return function (object, config, defaults) {

            if (defaults) {
                Ext.apply(object, defaults);
            }
            if (object && config && typeof config === 'object') {
                for (var key in config) {
                    object[key] = config[key];
                }
            }
            return object;
        };
    }
    return function (object, config, defaults) {

        if (defaults) {
            Ext.apply(object, defaults);
        }
        if (object && config && typeof config === 'object') {
            for (var key in config) {
                object[key] = config[key];
            }
            if (config.toString !== Object.prototype.toString) {
                object.toString = config.toString;
            }
            if (config.valueOf !== Object.prototype.valueOf) {
                object.valueOf = config.valueOf;
            }
        }
        return object;
    };
})();

//将一个无类型对象的属性和方法赋给Ext
Ext.apply(Ext, {
    platformVersion: '1.0',
    platformVersionDetail: {
        major: 1,
        minor: 0,
        patch: 3
    },
    userAgent: navigator.userAgent.toLowerCase(),
    cache: {},
    idSeed: 1000, //为页面元素生成id时所用的id计数器，其值作为id的一部分添加在末尾
    BLANK_IMAGE_URL: '',
    isStrict: document.compatMode == "CSS1Compat",

    windowId: 'ext-window', //为页面元素生成id时，如果元素是window，则用此id
    documentId: 'ext-document', ////为页面元素生成id时，如果元素是document，则用此id

    //可重复使用的空函数
    emptyFn: function () { },


    isSecure: /^https/i.test(window.location.protocol),

    //当document已完全初始化并准备执行动作时为true
    isReady: false,


    enableGarbageCollector: true,


    enableListenerCollection: true,

    //复制所有config的属性至object，如果object已有该属性，则不复制
    applyIf: function (object, config) {
        var property, undefined;

        if (object) {
            for (property in config) {
                if (object[property] === undefined) {
                    object[property] = config[property];
                }
            }
        }

        return object;
    }, //end of applyIf


    repaint: function () {
        var mask = Ext.getBody().createChild({
            cls: 'x-mask x-mask-transparent'
        });
        setTimeout(function () {
            mask.remove();
        }, 0);
    },

    /// <summary>
    /// 对页面元素生成唯一id，如果该元素已存在id，则不会再生成
    /// </summary>
    /// <param name="el">（可选的） 将要生成id的元素</param>
    /// <param name="prefix">（可选的） 该id的前缀（默认为 "ext-gen"）。</param>
    /// <return>生成的Id</return>
    id: function (el, prefix) {
        el = Ext.getDom(el) || {}; //根据传入的id,dom节点或Ext.Element返回对应的dom节点。返回类型： HTMLElement
        if (el === document) {
            el.id = this.documentId;
        }
        else if (el === window) {
            el.id = this.windowId;
        }
        el.id = el.id || ((prefix || 'ext-gen') + (++Ext.idSeed));
        return el.id;
    },

    //返回function (subclass, superclass, overrides)，
    //第一个参数是子类，第二个参数是父类，第三个参数是增加的方法
    extend: function () {

        //将o的属性复制给当前对象
        var inlineOverrides = function (o) {
            for (var m in o) {
                if (!o.hasOwnProperty(m)) {
                    continue;
                }
                this[m] = o[m];
            }
        };

        var objectConstructor = Object.prototype.constructor; //Object类的构造函数

        //这是实际返回的extend方法，该方法将会产生以下效果：
        //subclass是子类，它原先的prototype属性将被替换，因为在代码中它的prototype指像了另一个对象；
        //subclass的prototype指像一个新对象，该对象包含superclass.prototype的实例，该实例里面有superclass.prototype的属性，但是一般不直接通过该实例使用那些属性；
        //subclass新增一个superclass属性指向superclass.prototype，superclass是类（静态）属性，一般通过它使用superclass.prototype的属性；
        //overrides的属性将复制给subclass的prototype属性；
        //给subclass增加一个override类（静态)方法,它有一个参数，该方法的作用是将参数的属性赋给subclass.prototype
        //给给subclass增加一个extend类（静态)方法,它有一个参数，它的作用是创建一个继承于它的派生类，并将参数的属性赋给派生类的prototype。
        return function (subclass, superclass, overrides) {

            //判断v是否是一个对象,且该对象不是其他的内置对象,也不是HtmlElement对象，也不是function。在Ext对象中是这样定义的：
            /*isObject: function (v) {
                return !!v && !v.tagName && Object.prototype.toString.call(v) === '[object Object]';
            }*/
            //按照参数的顺序，第一个参数是子类，第二个参数是父类，第三个参数是一个对象，它包含要增加给子类的属性和方法。
            //而如果没有指定子类，即只有父类和增加的对象，则superclass就表示那个对象，所以就有了下面的判断，
            //这个判断其实是判断有没有指定子类，当没有指定子类时，就执行if语句中的语句。
            if (Ext.isObject(superclass)) {

                //执行了下面两天语句后，overrides就是包含附加属性的对象，superclass就是父类
                overrides = superclass;
                superclass = subclass;

                //如果overrides对象中有定义了构造函数，则subclass就是该构造函数，否则就新建一个构造函数，
                //传入参数调用superclass的构造函数
                subclass = overrides.constructor != objectConstructor
                    ? overrides.constructor // 自定义的构造函数
                    : function () { superclass.apply(this, arguments); }; // 没有自定义构造函数则创建新函数且新函数里运行父类
            }//当执行完if语句后，各参数就统一了

            if (!superclass) {//如果superclass为空，则抛出异常
                throw "Attempting to extend from a class which has not been loaded on the page.";
            }


            var F = function () { },
                subclassProto,
                superclassProto = superclass.prototype; // 父类原型

            F.prototype = superclassProto;

            //采用new F()新建一个含有所有superclass.prototype中的功能的对象，
            //这样就不会因改变subclass的prototype而影响影响到superclass的prototype了。
            subclassProto = subclass.prototype = new F(); //当new出一个F后，new出的对象中有一个属性，这个属性就是prototype指向的类（函数对象）的实例，prototype的属性就放在这个实例中

            //标志subclass的实例对象中的constructor为subclass
            subclassProto.constructor = subclass; 

            //通过这里，我们就可以类方法的形式调用其superclass中的方法
            subclass.superclass = superclassProto;

            if (superclassProto.constructor == objectConstructor) {
                superclassProto.constructor = superclass;
            }

            //在类上添加overrides指定的方法（多个方法），同名则覆盖
            /*override: function (origclass, overrides) {
                Ext.apply(origclass.prototype, overrides);
            }*/
            subclass.override = function (overrides) {//给subclass注册一个类（静态）方法override----------------------------
                Ext.override(subclass, overrides);
            };

            subclassProto.superclass = subclassProto.supr = (function () {
                return superclassProto;
            });

            subclassProto.override = inlineOverrides; //将参数o的属性复制给当前对象
            subclassProto.proto = subclassProto;

            subclass.override(overrides); //把overrides的属性和方法加到subclass的prototype对象中

            subclass.extend = function (o) {//为subclass加上一个类（静态）方法extend----------------------------------------
                return Ext.extend(subclass, o);
            };

            return subclass;
        };
    } (),

    //在类上添加overrides指定的方法（多个方法），同名则覆盖
    override: function (origclass, overrides) {
        Ext.apply(origclass.prototype, overrides);
    },

    //定义命名空间，实际上就是生成以命名空间名为名称的对象
    namespace: function () {
        var ln = arguments.length,
            i, value, split, x, xln, parts, object;

        for (i = 0; i < ln; i++) {
            value = arguments[i];
            parts = value.split("."); //split() 方法用于把一个字符串分割成字符串数组

            //生成跟对象，比如传过来的参数是a.b.c则在if-else中生成的object是名为a的对象
            if (window.Ext) {//这个判断极有可能是为了识别不同的浏览器
                object = window[parts[0]] = Object(window[parts[0]]); //根据数组元素的值创建以该值为名称的对象
            } else {
                //callee表示函数对象本身的引用，caller返回一个对函数的引用，该函数调用了当前函数。
                object = arguments.callee.caller.arguments[0];
            }

            //为根对象生成子对象，为子对象生成子对象，如此循环
            //比如前面生成了对象a，那么接下来的循环就生成a.b、a.b.c
            for (x = 1, xln = parts.length; x < xln; x++) {
                object = object[parts[x]] = Object(object[parts[x]]);
            }
        }
        return object; //声明作用域重要的不是返回值，而是执行过程中生成的以作用域为名的对象
    },

    //把一个对象转换为一串已编码的URL字符。例如Ext.urlEncode({foo: 1, bar: 2});变为"foo=1&bar=2"。
    //可选地，如果遇到属性的类型是数组的话，那么该属性对应的key就是每个数组元素的key，逐一进行“结对的”编码。
    //pre:（可选）添加到URL编码的字符串的前缀
    urlEncode: function (o, pre) {
        var empty,
            buf = [],
        //encodeURIComponent是全局函数。encodeURIComponent的目的是给URI进行编码。
        //与encodeURI函数的不同encodeURIComponent会对URI中具有特殊意义的字符也进行编码; 
        //; / ? : @ & = + $ , # 空格，其它的与encodeURI相同。
            e = encodeURIComponent;

        Ext.iterate(o, function (key, item) {
            empty = Ext.isEmpty(item);
            Ext.each(empty ? key : item, function (val) {
                buf.push('&', e(key), '=', (!Ext.isEmpty(val) && (val != key || !empty)) ? (Ext.isDate(val) ? Ext.encode(val).replace(/"/g, '') : e(val)) : '');
            });
        });

        if (!pre) {
            buf.shift();
            pre = '';
        }

        return pre + buf.join('');
    },

    //把一个已经encoded的URL字符串转换为对象。如Ext.urlDecode("foo=1&bar=2"); 
    //就是{foo: "1", bar: "2"}； Ext.urlDecode("foo=1&bar=2&bar=3&bar=4", false);
    //就是{foo: "1", bar: ["2", "3", "4"]}。
    //overwrite:重复名称的就当作为数组，如果该项为true就禁止该功能
    urlDecode: function (string, overwrite) {
        if (Ext.isEmpty(string)) {
            return {};
        }

        var obj = {},
            pairs = string.split('&'),
            d = decodeURIComponent,
            name,
            value;

        Ext.each(pairs, function (pair) {
            pair = pair.split('=');
            name = d(pair[0]);
            value = d(pair[1]);
            obj[name] = overwrite || !obj[name] ? value : [].concat(obj[name]).concat(value);
        });

        return obj;
    },


    htmlEncode: function (value) {
        return Ext.util.Format.htmlEncode(value);
    },


    htmlDecode: function (value) {
        return Ext.util.Format.htmlDecode(value);
    },

    /// <summary>
    /// 向一个URL查询字符串追加内容
    /// </summary>
    /// <param name="url">要追加内容的URL</param>
    /// <param name="s">追加到URL的内容</param>
    /// <return>产生的URL</return>
    urlAppend: function (url, s) {
        if (!Ext.isEmpty(s)) {
            return url + (url.indexOf('?') === -1 ? '?' : '&') + s;
        }
        return url;
    },


    /// <summary>
    /// 将任何可迭代的（数字索引和长度属性）转换成真正的数组，不要将该函数用于字符串。
    /// 该实现依赖于"abc"[0],这是IE不支持的。对于字符串，用"abc".match(/./g) => [a,b,c]来代替
    /// </summary>
    /// <param name="array">要转变成数组的迭代对象</param>
    /// <param name="start">一个数字，指定转变的开始处</param>
    /// <param name="end">一个数字，指定转变的结束处</param>
    /// <return>转变后的数组</return>
    toArray: function (array, start, end) {
        return Array.prototype.slice.call(array, start || 0, end || array.length);
    },

    /// <summary>
    /// 遍历一个数组，调用所提供的方法
    /// </summary>
    /// <param name="array">要遍历的数组，如果不是数组，则提供的方法只调用一次</param>
    /// <param name="fn">每个迭代都调用的方法，如果函数返回false，迭代将停止，次方法返回当前索引</param>
    /// <param name="scope">函数的作用域，默认为当前遍历的数组元素</param>
    each: function (array, fn, scope) {
        if (Ext.isEmpty(array, true)) {
            return 0;
        }
        if (!Ext.isIterable(array) || Ext.isPrimitive(array)) {//如果array不是一个迭代对象，或array是一个字符串，数字或布尔对象
            array = [array];
        }
        for (var i = 0, len = array.length; i < len; i++) {
            //fn有三个参数：当前遍历的元素、当前元素的索引和当前遍历的数组
            if (fn.call(scope || array[i], array[i], i, array) === false) {
                return i;
            }
        }
        return true;
    },

    /// <summary>
    /// 遍历一个数组中的元素或一个对象中的属性，如果只遍历数组，用each方法更好
    /// </summary>
    /// <param name="obj">要遍历的对象或数组</param>
    /// <param name="fn">每个迭代都调用的函数，如果函数返回false，迭代将停止</param>
    /// <param name="scope">函数的作用域，默认为当前遍历的对象</param>
    iterate: function (obj, fn, scope) {
        if (Ext.isEmpty(obj)) {
            return;
        }
        if (Ext.isIterable(obj)) {//遍历数组
            Ext.each(obj, fn, scope);
            return;
        }
        else if (Ext.isObject(obj)) {//遍历对象
            for (var prop in obj) {
                if (obj.hasOwnProperty(prop)) {//判断一个对象是否具有指定名称的属性
                    //fn方法有三个属性：当前遍历的属性名、属性值和当前遍历的对象
                    if (fn.call(scope || obj, prop, obj[prop], obj) === false) {
                        return;
                    }
                }
            }
        }
    },


    pluck: function (arr, prop) {
        var ret = [];
        Ext.each(arr, function (v) {
            ret.push(v[prop]);
        });
        return ret;
    },

    //返回当前文档的body部分，类型为Ext.Element
    getBody: function () {
        return Ext.get(document.body || false);
    },

    //返回当前文档的头部，类型为Ext.Element
    getHead: function () {
        var head;

        return function () {
            if (head == undefined) {
                head = Ext.get(DOC.getElementsByTagName("head")[0]);
            }

            return head;
        };
    } (),

    //返回当前的 HTML document 对象，对象类型为Ext.Element
    getDoc: function () {
        return Ext.get(document); //Ext.get = El.get;
    },

    //通过id查找一个现有组件，组件类型为Ext.Component
    getCmp: function (id) {
        return Ext.ComponentMgr.get(id);
    },

    //返回移动设备的当前方向，（移动设备横着放还是竖着放）
    getOrientation: function () {
        return window.innerHeight > window.innerWidth ? 'portrait' : 'landscape';
    },

    isIterable: function (v) {
        if (!v) {
            return false;
        }

        if (Ext.isArray(v) || v.callee) {
            return true;
        }

        if (/NodeList|HTMLCollection/.test(Object.prototype.toString.call(v))) {
            return true;
        }



        return ((typeof v.nextNode != 'undefined' || v.item) && Ext.isNumber(v.length)) || false;
    },

    //如果v是数字，则返回v，如果v不是数字，则返回defaultValue
    //v应该是一个数字，但会妥善处理任何类型
    num: function (v, defaultValue) {
        v = Number(Ext.isEmpty(v) || Ext.isArray(v) || typeof v == 'boolean' || (typeof v == 'string' && Ext.util.Format.trim(v).length == 0) ? NaN : v);
        return isNaN(v) ? defaultValue : v;
    },

    //判断value是否是null、undefined、空数组、长度为0的字符串
    //如果allowBlank为true，则不包括空字符串，默认为false
    isEmpty: function (value, allowBlank) {
        var isNull = value == null,
            emptyArray = (Ext.isArray(value) && !value.length),
            blankAllowed = !allowBlank ? value === '' : false; //先执行!allowBlank

        return isNull || emptyArray || blankAllowed;
    },

    //判断v是否是一个Array对象
    isArray: function (v) {
        return Object.prototype.toString.apply(v) === '[object Array]';
    },

    //判断v是否是一个Date对象
    isDate: function (v) {
        return Object.prototype.toString.apply(v) === '[object Date]';
    },

    //判断v是否是一个对象,且该对象不是其他的内置对象,也不是HtmlElement对象
    isObject: function (v) {
        return !!v && !v.tagName && Object.prototype.toString.call(v) === '[object Object]';
    },

    //判断v是否是一个字符串，数字或布尔对象。
    isPrimitive: function (v) {
        return Ext.isString(v) || Ext.isNumber(v) || Ext.isBoolean(v);
    },

    //判断v是否是一个函数
    isFunction: function (v) {
        return Object.prototype.toString.apply(v) === '[object Function]';
    },

    //判断v是否是一个数字，且v不是NaN（非数字），或者是正、负无穷大的数
    isNumber: function (v) {
        return Object.prototype.toString.apply(v) === '[object Number]' && isFinite(v);
    },

    //判断v是否是一个字符串
    isString: function (v) {
        return typeof v === 'string';
    },

    //判断v是否是布尔对象
    isBoolean: function (v) {
        return Object.prototype.toString.apply(v) === '[object Boolean]';
    },

    //判断v是否是HTMLElement对象，
    // 在DOM标准中，每个HTML元素都是继承自HTMLElement
    isElement: function (v) {
        return v ? !!v.tagName : false;
    },

    //判断v是否已定义
    isDefined: function (v) {
        return typeof v !== 'undefined';
    },

    //移除各参数的事件监听器，或将它们从DOM结构中删除，或调用它们的destroy方法
    //这种方法主要用于Ext.Element和Ext.Component类型的参数，但任何Ext.util.Observable
    //的子类可以作为参数
    destroy: function () {
        var ln = arguments.length,
            i, arg;

        for (i = 0; i < ln; i++) {
            arg = arguments[i];
            if (arg) {
                //如果arg是数组，在用destroy方法迭代销毁数组中的每个元素
                if (Ext.isArray(arg)) {
                    this.destroy.apply(this, arg);
                }
                //如果arg有destroy方法，则调用它自身的destroy方法
                else if (Ext.isFunction(arg.destroy)) {
                    arg.destroy();
                }
                //如果arg存在dom（The DOM element）属性
                else if (arg.dom) {
                    arg.remove(); //删除arg的DOM引用
                }
            }
        }
    }
});

Ext.apply(Ext, {

    version: '1.1.0',
    versionDetail: {
        major: 1,
        minor: 1,
        patch: 0
    },


    setup: function (config) {
        if (config && typeof config == 'object') {
            if (config.addMetaTags !== false) {
                this.addMetaTags(config);
            }

            if (Ext.isFunction(config.onReady)) {
                var me = this;

                Ext.onReady(function () {
                    var args = arguments;

                    if (config.fullscreen !== false) {
                        Ext.Viewport.init(function () {
                            config.onReady.apply(me, args);
                        });
                    }
                    else {
                        config.onReady.apply(this, args);
                    }
                }, config.scope);
            }
        }
    },


    getDom: function (el) {
        if (!el || !document) {
            return null;
        }

        return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
    },


    removeNode: function (node) {
        if (node && node.parentNode && node.tagName != 'BODY') {
            Ext.EventManager.removeAll(node);
            node.parentNode.removeChild(node);
            delete Ext.cache[node.id];
        }
    },


    addMetaTags: function (config) {
        if (!Ext.isObject(config)) {
            return;
        }

        var head = Ext.get(document.getElementsByTagName('head')[0]),
            tag, precomposed;


        if (!Ext.is.Desktop) {
            tag = Ext.get(document.createElement('meta'));
            tag.set({
                name: 'viewport',
                content: 'width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0;'
            });
            head.appendChild(tag);
        }


        if (Ext.is.iOS) {

            if (config.fullscreen !== false) {
                tag = Ext.get(document.createElement('meta'));
                tag.set({
                    name: 'apple-mobile-web-app-capable',
                    content: 'yes'
                });
                head.appendChild(tag);

                if (Ext.isString(config.statusBarStyle)) {
                    tag = Ext.get(document.createElement('meta'));
                    tag.set({
                        name: 'apple-mobile-web-app-status-bar-style',
                        content: config.statusBarStyle
                    });
                    head.appendChild(tag);
                }
            }


            if (config.tabletStartupScreen && Ext.is.iPad) {
                tag = Ext.get(document.createElement('link'));
                tag.set({
                    rel: 'apple-touch-startup-image',
                    href: config.tabletStartupScreen
                });
                head.appendChild(tag);
            }

            if (config.phoneStartupScreen && !Ext.is.iPad) {
                tag = Ext.get(document.createElement('link'));
                tag.set({
                    rel: 'apple-touch-startup-image',
                    href: config.phoneStartupScreen
                });
                head.appendChild(tag);
            }


            if (config.icon) {
                config.phoneIcon = config.tabletIcon = config.icon;
            }

            precomposed = (config.glossOnIcon === false) ? '-precomposed' : '';
            if (Ext.is.iPad && Ext.isString(config.tabletIcon)) {
                tag = Ext.get(document.createElement('link'));
                tag.set({
                    rel: 'apple-touch-icon' + precomposed,
                    href: config.tabletIcon
                });
                head.appendChild(tag);
            }
            else if (!Ext.is.iPad && Ext.isString(config.phoneIcon)) {
                tag = Ext.get(document.createElement('link'));
                tag.set({
                    rel: 'apple-touch-icon' + precomposed,
                    href: config.phoneIcon
                });
                head.appendChild(tag);
            }
        }
    }
});

Ext.encode = Ext.util.JSON.encode;

Ext.decode = Ext.util.JSON.decode;
